/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package leveldb

import (
	"fmt"
	"strconv"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/filter"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/util"
	"hundsun.com/hsl/hschain/common/config"
	"hundsun.com/hsl/hschain/common/types"
	dbm "hundsun.com/hsl/hschain/store/db"
)

func init() {
	dbm.Register("leveldb", NewLeveldb)
}

//Leveldb db
type Leveldb struct {
	db *leveldb.DB
}

//NewLeveldb new
func NewLeveldb(cfg *config.PluginConfig) (dbm.Database, error) {
	if cfg == nil {
		panic(fmt.Sprintln("NewLeveldb fail", "PluginConfig is nil"))
	}

	dbPath := "datadir"
	if v, ok := cfg.Parameters["dbPath"]; ok {
		dbPath = v
	}

	cache := 128
	if v, ok := cfg.Parameters["cache"]; ok {
		h, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			cache = int(h)
		}
	}
	if cache < 16 {
		cache = 16
	}

	handles := cache

	// Open the db and recover any potential corruptions
	db, err := leveldb.OpenFile(dbPath, &opt.Options{
		OpenFilesCacheCapacity: handles,
		BlockCacheCapacity:     cache / 2 * opt.MiB,
		WriteBuffer:            cache / 2 * opt.MiB, // Two of these are used internally
		Filter:                 filter.NewBloomFilter(10),
	})
	if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
		db, err = leveldb.RecoverFile(dbPath, nil)
	}
	if err != nil {
		return nil, err
	}

	ldb := &Leveldb{
		db: db,
	}
	return ldb, nil
}

//Get get
func (db *Leveldb) Get(key []byte) ([]byte, error) {
	res, err := db.db.Get(key, nil)
	if err != nil {
		if err == errors.ErrNotFound {
			return nil, types.ErrNotFound
		}
		return nil, err
	}
	return res, nil
}

// Gets 批量获取
func (db *Leveldb) Gets(keys [][]byte) ([][]byte, error) {
	values := make([][]byte, len(keys))
	for i, key := range keys {
		v, err := db.db.Get(key, nil)
		if err == nil {
			values[i] = cloneByte(v)
		}
	}
	return values, nil
}

//Set set
func (db *Leveldb) Set(key []byte, value []byte) error {
	err := db.db.Put(key, value, nil)
	if err != nil {
		return err
	}
	return nil
}

//Has has
func (db *Leveldb) Has(key []byte) (bool, error) {
	return db.db.Has(key, nil)
}

//Delete 删除
func (db *Leveldb) Delete(key []byte) error {
	err := db.db.Delete(key, nil)
	if err != nil {
		return err
	}
	return nil
}

//DB db
func (db *Leveldb) DB() *leveldb.DB {
	return db.db
}

//Close 关闭
func (db *Leveldb) Close() {
	err := db.db.Close()
	if err != nil {
		fmt.Println("Close", "error", err)
	}
}

//NewBatch new
func (db *Leveldb) NewBatch(sync bool) dbm.Batch {
	batch := &leveldb.Batch{}
	wop := &opt.WriteOptions{Sync: sync}
	return &levelBatch{db: db, batch: batch, wop: wop}
}

// NewIteratorWithRange new range iterator
func (db *Leveldb) NewIteratorWithRange(start []byte, limit []byte) dbm.Iterator {
	return db.db.NewIterator(&util.Range{Start: start, Limit: limit}, nil)
}

// NewIteratorWithPrefix new prefix iterator
func (db *Leveldb) NewIteratorWithPrefix(prefix []byte) dbm.Iterator {
	return db.db.NewIterator(util.BytesPrefix(prefix), nil)
}

// List ...
type List struct {
	*Leveldb
}

// NewList new list
func (db *Leveldb) NewList() dbm.Lister {
	return &List{db}
}

// ListRange count < 0 则对取值数量不做限制
func (db *List) ListRange(start, limit []byte, count int32, opt dbm.Option) ([][]byte, [][]byte, error) {
	it := db.NewIteratorWithRange(start, limit)
	defer it.Release()
	return dbm.List(it, count, opt)
}

// ListPrefix count < 0 则对取值数量不做限制
func (db *List) ListPrefix(prefix []byte, count int32, opt dbm.Option) ([][]byte, [][]byte, error) {
	it := db.NewIteratorWithPrefix(prefix)
	defer it.Release()
	return dbm.List(it, count, opt)
}

// RangeCount 查询Range范围内迭代个数
func (db *List) RangeCount(start, limit []byte) int64 {
	it := db.NewIteratorWithRange(start, limit)
	defer it.Release()
	return dbm.ListCount(it)
}

// PrefixCount 查询prefix迭代个数
func (db *List) PrefixCount(prefix []byte) int64 {
	it := db.NewIteratorWithPrefix(prefix)
	defer it.Release()
	return dbm.ListCount(it)
}

type levelBatch struct {
	db    *Leveldb
	batch *leveldb.Batch
	wop   *opt.WriteOptions
	size  int
	len   int
}

// ValueSize 值大小
func (b *levelBatch) ValueSize() int {
	return b.size
}

// Set batch set
func (b *levelBatch) Set(key, value []byte) error {
	b.batch.Put(key, value)
	b.size += len(value)
	b.size += len(key)
	b.len += len(value)
	return nil
}

// Delete batch delete
func (b *levelBatch) Delete(key []byte) error {
	b.batch.Delete(key)
	b.size += len(key)
	b.len++
	return nil
}

// Write batch write
func (b *levelBatch) Write() error {
	return b.db.db.Write(b.batch, b.wop)
}

// ValueLen  batch数量
func (b *levelBatch) ValueLen() int {
	return b.len
}

// Reset batch reset
func (b *levelBatch) Reset() {
	b.batch.Reset()
	b.len = 0
	b.size = 0
}

func cloneByte(v []byte) []byte {
	value := make([]byte, len(v))
	copy(value, v)
	return value
}
